home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2005 March / Macworld CD March 2005 - Marathon Trilogy.iso / Shareware World / Text Processing / HexEdit Release.sit / HexEdit Release / Project / Source / EditRoutines.c < prev    next >
Encoding:
C/C++ Source or Header  |  2004-10-31  |  25.1 KB  |  1,070 lines  |  [TEXT/CWIE]

  1. /*
  2.  * The contents of this file are subject to the Mozilla Public
  3.  * License Version 1.1 (the "License"); you may not use this file
  4.  * except in compliance with the License. You may obtain a copy of
  5.  * the License at http://www.mozilla.org/MPL/
  6.  * 
  7.  * Software distributed under the License is distributed on an "AS
  8.  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
  9.  * implied. See the License for the specific language governing
  10.  * rights and limitations under the License.
  11.  * 
  12.  * The Original Code is Copyright 1993 Jim Bumgardner.
  13.  * 
  14.  * The Initial Developer of the Original Code is Jim Bumgardner
  15.  * Portions created by Lane Roathe are
  16.  * Copyright (C) Copyright © 1996-2002.
  17.  * All Rights Reserved.
  18.  *
  19.  * Modified: $Date: 2004/10/31 19:09:58 $
  20.  * Revision: $Id: EditRoutines.c,v 1.21 2004/10/31 19:09:58 raving Exp $
  21.  *
  22.  * Contributor(s):
  23.  *        Lane Roathe
  24.  *        Nick Shanks
  25.  *        Greg Branche
  26.  */
  27.  
  28. // 05/10/01 - GAB: MPW environment support
  29. #ifdef __MPW__
  30. #include "MPWIncludes.h"
  31. #endif
  32.  
  33. #include <ctype.h>
  34.  
  35. #include "EditScrollbar.h"
  36. #include "Menus.h"
  37. #include "Prefs.h"
  38. #include "Utility.h"
  39.  
  40. #include "EditRoutines.h"
  41.  
  42. // Global Vars
  43.  
  44. UndoRecord gUndo, gRedo;
  45.  
  46. static EditChunk    **_scrapChunk;
  47.  
  48.  
  49. /*** LOAD FILE ***/
  50. // Assumes theWin has just been opened, file is open, fileSize field is correct
  51. void LoadFile( EditWindowPtr dWin )
  52. {
  53.     EditChunk    **nc;
  54.     long        count, chunkSize, pos;
  55.     Boolean        once = true;
  56.  
  57.     count = dWin->fileSize;
  58.     pos = 0L;
  59.  
  60.     // LR: - if empty fork, just create a chunk so we can insert data!
  61.  
  62. //    if( !count )
  63. //        count = 1;
  64.  
  65.     while( count || once )
  66.     {
  67.         if( count <= kChunkSize )
  68.             chunkSize = count;
  69.         else
  70.             chunkSize = kChunkSize;
  71.         count -= chunkSize;
  72.         nc = NewChunk( chunkSize, pos, pos, CT_Original );
  73.         dWin->firstChunk = AppendChunk( dWin->firstChunk, nc );
  74.         pos += chunkSize;
  75.         once = false;
  76.     }
  77.     dWin->curChunk = dWin->firstChunk;
  78. }
  79.  
  80. /*** UNLOAD FILE ***/
  81. void UnloadFile( EditWindowPtr dWin )
  82. {
  83.     EditChunk    **cc, **bc;
  84.     cc = dWin->firstChunk;
  85.     while( cc )
  86.     {
  87.         bc = ( *cc )->next;
  88.         DisposeChunk( dWin, cc );
  89.         cc = bc;
  90.     }
  91.     dWin->firstChunk = dWin->curChunk = NULL;
  92. }
  93.  
  94. /*** NEW CHUNK ***/
  95. EditChunk** NewChunk( long size, long addr, long filePos, short type )
  96. {
  97.     EditChunk **nc;
  98.     nc = ( EditChunk ** ) NewHandleClear( sizeof( EditChunk ) );
  99.     if( !nc )
  100.     {
  101.         ErrorAlert( ES_Caution, errMemory );
  102.         return NULL;
  103.     }
  104.     ( *nc )->type = type;
  105.     ( *nc )->size = size;
  106.     ( *nc )->addr = addr;
  107.     ( *nc )->filePos = filePos;
  108.     ( *nc )->lastCtr = -1;
  109.     if( type == CT_Unwritten )
  110.     {
  111.         ( *nc )->loaded = true;
  112.         ( *nc )->allocSize = size;
  113.         ( *nc )->data = NewHandleClear( size );
  114.         if( !( *nc )->data )
  115.         {
  116.             ErrorAlert( ES_Caution, errMemory );
  117.             DisposeHandle( (Handle) nc );
  118.             return NULL;
  119.         }
  120.     }
  121.     else
  122.     {
  123.         ( *nc )->loaded = false;
  124.         ( *nc )->data = NULL;
  125.         ( *nc )->allocSize = 0L;
  126.     }
  127.     return nc;
  128. }
  129.  
  130. /*** DISPOSE CHUNK ***/
  131. void DisposeChunk( EditWindowPtr dWin, EditChunk **cc )
  132. {
  133.     if( dWin && (*cc)->loaded )
  134.             UnloadChunk( dWin, cc, false );
  135.  
  136.     DisposeHandle( (Handle)cc );
  137. }
  138.  
  139. /*** APPEND CHUNK ***/
  140. EditChunk** AppendChunk( EditChunk **list, EditChunk **chunk )
  141. {
  142.     if( list )
  143.     {
  144.         register EditChunk    **curChunk;
  145.         curChunk = list;
  146.         while( ( *curChunk )->next )
  147.             curChunk        = ( *curChunk )->next;
  148.         ( *curChunk )->next    = chunk;
  149.         ( *chunk )->prev    = curChunk;
  150.         ( *chunk )->next    = NULL;
  151.     }
  152.     else
  153.     {
  154.         list = chunk;
  155.         ( *chunk )->next    = ( *chunk )->prev = NULL;
  156.     }
  157.     return list;
  158. }
  159.  
  160. /*** SET CURRENT CHUNK ***/
  161. void SetCurrentChunk( EditWindowPtr dWin, long addr )
  162. {
  163.     register EditChunk **cc;
  164.     cc = GetChunkByAddr( dWin, addr );
  165.     dWin->curChunk = cc;
  166. }
  167.  
  168. /*** GET CHUNK BY ADDRESS ***/
  169. EditChunk** GetChunkByAddr( EditWindowPtr dWin, long addr )
  170. {
  171.     register EditChunk **cc;
  172.  
  173.     // Does current chunk contain address?
  174.     if( dWin->curChunk && addr >= ( *dWin->curChunk )->addr )
  175.         cc = dWin->curChunk;
  176.     else    // Otherwise, start from beginning of chain
  177.         cc = dWin->firstChunk;
  178.  
  179.     // Search chunck list for chunck with our address
  180.     while( cc )
  181.     {
  182.         if( addr < ( *cc )->addr+( *cc )->size )
  183.             break;
  184.         else
  185.         {
  186.             if( ( *cc )->next )
  187.                 cc = ( *cc )->next;
  188.             else
  189.                 return cc;
  190.         }
  191.     }
  192.     return cc;
  193. }
  194.  
  195. /*** GET BYTE ***/
  196. Byte GetByte( EditWindowPtr dWin, long addr )
  197. {
  198.     register EditChunk **cc;
  199.     if( ( cc = GetChunkByAddr( dWin, addr ) ) != NULL )
  200.     {
  201.         // Correct Chunk
  202.         if( !( *cc )->loaded ) LoadChunk( dWin, cc );
  203.         if( ( *cc )->lastCtr != dWin->useCtr )
  204.         {
  205.             // Update the Counter
  206.             ++dWin->useCtr;
  207.             ( *cc )->lastCtr = dWin->useCtr;
  208.         }
  209.         return (Byte) ( *( *cc )->data )[addr - ( *cc )->addr];
  210.     }
  211.     return (Byte)-1;
  212. }
  213.  
  214. /*** LOAD CHUNK ***/
  215. void LoadChunk( EditWindowPtr dWin, EditChunk **cc )
  216. {
  217.     long    count;
  218.     OSErr    error;
  219.     short    refNum;
  220.  
  221.     if( ( *cc )->loaded )
  222.         return;
  223.  
  224.     // Check if we can fit within MaxFileRam, if not, deallocate old chunks
  225.     // until we're ok
  226.     while( dWin->totLoaded+( *cc )->size > kMaxFileRAM )
  227.         UnloadLeastUsedChunk( dWin );
  228.  
  229.     ( *cc )->data = NewHandleClear( ( *cc )->size );
  230.     if( !( *cc )->data )
  231.     {
  232.         ErrorAlert( ES_Caution, errMemory );
  233.         ( *cc )->allocSize = 0L;
  234.         ( *cc )->loaded = false;
  235.     }
  236.     else
  237.     {
  238.         if( ( *cc )->type == CT_Work ) refNum = dWin->workRefNum;
  239.         else refNum = dWin->refNum;
  240.         ( *cc )->allocSize = ( *cc )->size;
  241.         ( *cc )->loaded = true;
  242.         if( ( error = SetFPos( refNum, fsFromStart, ( *cc )->filePos ) ) != noErr )
  243.             ErrorAlert( ES_Caution, errSeek, error );
  244.         count = ( *cc )->size;
  245.         if( count )    //LR 1.73 :empty file is OK, don't bring up an error by trying to read nothing!
  246.         {
  247.             dWin->totLoaded += count;
  248.             if( ( error = FSRead( refNum, &count, *( *cc )->data ) ) != noErr )
  249.                 ErrorAlert( ES_Caution, errRead, error );
  250.         }
  251.     }
  252. }
  253.  
  254. /*** UNLOAD LEAST USED CHUNK ***/
  255. void UnloadLeastUsedChunk( EditWindowPtr dWin )
  256. {
  257.     EditChunk    **cc, **oc=NULL;
  258.     oc = cc = dWin->firstChunk;
  259.     while( cc )
  260.     {
  261.         if( ( *cc )->loaded && ( !( *oc )->loaded || ( *cc )->lastCtr < ( *oc )->lastCtr ) )
  262.             oc = cc;
  263.         cc = ( *cc )->next;
  264.     }
  265.     if( oc ) UnloadChunk( dWin, oc, true );
  266. }
  267.  
  268. /*** UNLOAD CHUNK ***/
  269. void UnloadChunk( EditWindowPtr dWin, EditChunk    **cc, Boolean writeFlag )
  270. {
  271.     long    count;
  272.     OSErr    error;
  273.  
  274.     if( cc && ( *cc )->loaded && ( *cc )->data )
  275.     {
  276.         if( writeFlag && ( *cc )->type == CT_Unwritten )
  277.         {
  278.             // Record New Chunks in Work File
  279.             error = SetFPos( dWin->workRefNum, fsFromStart, dWin->workBytesWritten );
  280.             if( error )
  281.                 ErrorAlert( ES_Caution, errSetFPos, error );
  282.             count = ( *cc )->size;
  283.             error = FSWrite( dWin->workRefNum, &count, *( *cc )->data );
  284.             if( error )
  285.                 ErrorAlert( ES_Caution, errWrite, error );
  286.             ( *cc )->type = CT_Work;
  287.             ( *cc )->filePos = dWin->workBytesWritten;
  288.             dWin->workBytesWritten += count;
  289.         }
  290.  
  291.         dWin->totLoaded -= ( *cc )->size;
  292.         ( *cc )->loaded = false;
  293.         DisposeHandle( ( *cc )->data );
  294.         ( *cc )->data = NULL;
  295.         ( *cc )->allocSize = 0L;
  296.     }
  297. }
  298.  
  299. /*** REWRITE ADDRESS CHAIN ***/
  300. void RewriteAddressChain( EditChunk **fc )
  301. {
  302.     EditChunk    **nc;
  303.     // Rewrite Addresses of chunks starting from fc
  304.     nc = ( *fc )->next;
  305.     while( nc )
  306.     {
  307.         ( *nc )->addr = ( *( *nc )->prev )->addr + ( *( *nc )->prev )->size;
  308.         nc = ( *nc )->next;
  309.     }
  310. }
  311.  
  312. /*** REMOVE SELECTION ***/
  313. void RemoveSelection( EditWindowPtr dWin )
  314. {
  315.     EditChunk **fc, **ec, **nc, **tc;
  316.  
  317.     //LR     //LR 190 -- renamed from DeleteSelection, more appropriate name
  318.     if( dWin->readOnlyFlag )
  319.     {
  320.         ErrorAlert( ES_Stop, errReadOnly );
  321.         return;
  322.     }
  323.  
  324.     if( dWin->endSel == dWin->startSel ) return;
  325.  
  326.     // Identify Starting Chunk
  327.     fc = GetChunkByAddr( dWin, dWin->startSel );
  328.     dWin->curChunk = fc;        // Optimize chunk searches
  329.  
  330.     // Identify Ending Chunk
  331.     ec = GetChunkByAddr( dWin, dWin->endSel );
  332.  
  333.     // If Chunks are the same
  334.     if( fc == ec )
  335.     {
  336.         // If chunk is unwritten
  337.         if( ( *fc )->type == CT_Unwritten )
  338.         {
  339.             BlockMove( *( *fc )->data + ( dWin->endSel - ( *fc )->addr ), 
  340.                         *( *fc )->data + ( dWin->startSel - ( *fc )->addr ), 
  341.                         ( *fc )->size - ( dWin->endSel - ( *fc )->addr ) );
  342.             ( *fc )->size -= dWin->endSel - dWin->startSel;
  343.         }
  344.         else
  345.         {
  346.             UnloadChunk( dWin, fc, true );
  347.             // Split into two chunks
  348.             nc = NewChunk( ( *fc )->size - ( dWin->endSel - ( *fc )->addr ), 
  349.                             0, 
  350.                             ( *fc )->filePos + ( dWin->endSel - ( *fc )->addr ), 
  351.                             ( *fc )->type );
  352.             ( *nc )->prev = fc;
  353.             ( *nc )->next = ( *fc )->next;
  354.             if( ( *nc )->next )
  355.                 ( *( *nc )->next )->prev = nc;
  356.             ( *fc )->next = nc;
  357.             ( *fc )->size = dWin->startSel - ( *fc )->addr;
  358.         }
  359.     }
  360.     else
  361.     {
  362.         // Truncate end of first Chunk
  363.         ( *fc )->size = dWin->startSel - ( *fc )->addr;
  364.         // Unlink & Dispose Middle Chunks, If Any
  365.         nc = ( *fc )->next;
  366.         while( nc != ec )
  367.         {
  368.             tc = ( *nc )->next;
  369.             DisposeChunk( dWin, nc );
  370.             nc = tc;
  371.         }
  372.         ( *ec )->prev = fc;
  373.         ( *fc )->next = ec;
  374.         // Truncate beg of end chunk
  375.         if( ( *ec )->type == CT_Unwritten )
  376.         {
  377.             long    offset;
  378.             offset = dWin->endSel - ( *ec )->addr;
  379. // LR: -- fix according to feedback from Jonathan Wright
  380. //             BlockMove( *( *ec )->data, *( *ec )->data+offset, ( *ec )->size - offset );
  381.             BlockMove( *( *ec )->data+offset, *( *ec )->data, ( *ec )->size - offset );
  382.             ( *ec )->size -= offset;
  383.         }
  384.         else
  385.         {
  386.             long    offset;
  387.             offset = dWin->endSel - ( *ec )->addr;
  388.             UnloadChunk( dWin, ec, true );
  389.             ( *ec )->filePos += offset;
  390.             ( *ec )->size -= offset;
  391.         }
  392.     }
  393.  
  394.     dWin->fileSize -= ( dWin->endSel - dWin->startSel );
  395.  
  396.     RewriteAddressChain( fc );
  397.  
  398.     // Modify Current Selection such that    endSel = firstSel
  399.     dWin->endSel = dWin->startSel;
  400.     dWin->dirtyFlag = true;
  401. }
  402.  
  403. // Assumes selection Point is already 0 chars wide...
  404.  
  405. /*** INSERT CHARACTER ***/
  406. void InsertCharacter( EditWindowPtr dWin, short charCode )
  407. {
  408.     EditChunk **fc, **ec, **nc;    // LR: remove tc to fix warnings
  409.  
  410.     //LR 180 -- first, this is useless on read-only files!
  411.     if( dWin->readOnlyFlag )
  412.     {
  413.         ErrorAlert( ES_Stop, errReadOnly );
  414.         return;
  415.     }
  416.  
  417.     // !! Remember Current State for Undo
  418.  
  419.  
  420.     // Insert Character into List
  421.     //     Identify current chunk - optimize so that if char is between
  422.     //         chunks, pick the unwritten one of the two...
  423.  
  424.     // Identify Starting Chunk
  425.     fc = GetChunkByAddr( dWin, dWin->startSel );
  426.  
  427.     //     Identify current chunk - optimize so that if char is between
  428.     //     chunks, pick the unwritten one of the two... - this way, if I keep typing
  429.     //     characters, I won't generate a bunch of 1 byte chunks.
  430.     if( dWin->startSel - ( *fc )->addr == 0 &&
  431.         ( *fc )->prev && ( *fc )->type != CT_Unwritten &&
  432.     ( *( *fc )->prev )->type == CT_Unwritten )
  433.         fc = ( *fc )->prev;
  434.     dWin->curChunk = fc;        // Optimize chunk searches
  435.  
  436.     //     If current chunk is not unwritten
  437.     if( ( *fc )->type != CT_Unwritten )
  438.     {
  439.         // Unload it
  440.         UnloadChunk( dWin, fc, true );
  441.  
  442.         if( dWin->startSel > ( *fc )->addr )
  443.         {
  444.  
  445.             // Split into two chunks
  446.             if( dWin->startSel < ( *fc )->addr + ( *fc )->size )
  447.             {
  448.                 ec = NewChunk( ( *fc )->size - ( dWin->startSel - ( *fc )->addr ), 0, 
  449.                                 ( *fc )->filePos + ( dWin->startSel - ( *fc )->addr ), 
  450.                                 ( *fc )->type );
  451.                 ( *ec )->prev = fc;
  452.                 ( *ec )->next = ( *fc )->next;
  453.                 if( ( *ec )->next )
  454.                     ( *( *ec )->next )->prev = ec;
  455.                 ( *fc )->next = ec;
  456.             }
  457.             else ec = ( *fc )->next;
  458.  
  459.             ( *fc )->size = dWin->startSel - ( *fc )->addr;
  460.         }
  461.         else
  462.         {
  463.             ec = fc;
  464.             fc = ( *fc )->prev;
  465.         }
  466.  
  467.         // Add New unwritten chunk in middle with 0 size
  468.         nc = NewChunk( 0, 0, 0, CT_Unwritten );
  469.         if( fc )
  470.         {
  471.             ( *fc )->next = nc;
  472.             ( *nc )->addr = ( *fc )->addr + ( *fc )->size;
  473.         }
  474.         else
  475.             dWin->firstChunk = nc;
  476.         if( ec )
  477.             ( *ec )->prev = nc;
  478.         ( *nc )->prev = fc;
  479.         ( *nc )->next = ec;
  480.         // current chunk = new chunk
  481.         dWin->curChunk = nc;
  482.         fc = nc;
  483.     }
  484.  
  485.     //     Expand Ptr if Necessary
  486.     if( ( *fc )->allocSize <= ( *fc )->size )
  487.     {
  488.         ( *fc )->allocSize += kAllocIncrement;        // !! consider expanding as size goes up
  489.         SetHandleSize( ( *fc )->data, ( *fc )->allocSize );
  490.     }
  491.  
  492.     // Make Room for Character if necessary
  493.     if( dWin->startSel < ( *fc )->addr + ( *fc )->size )
  494.         BlockMove( *( *fc )->data + ( dWin->startSel - ( *fc )->addr ), 
  495.                     *( *fc )->data + ( 1+( dWin->startSel - ( *fc )->addr ) ), 
  496.                     ( *fc )->addr + ( *fc )->size - dWin->startSel );
  497.  
  498.     //     Insert Char into buffer
  499.     ( *( *fc )->data )[dWin->startSel - ( *fc )->addr] = charCode;
  500.  
  501.     //     Update Fields in this chunk
  502.     ( *fc )->size++;
  503.     dWin->fileSize++;
  504.  
  505.     // Set Dirty Flag
  506.     dWin->dirtyFlag = true;
  507.  
  508.     //     Update addr fields of following chunks
  509.     RewriteAddressChain( fc );
  510.  
  511.     // Increment current Selection
  512.     dWin->startSel++;
  513.     dWin->endSel++;
  514.  
  515.  
  516.     // Update Display
  517.     ScrollToSelection( dWin, dWin->startSel, false );
  518. }
  519.  
  520. /*** RELEASE EDIT SCRAP ***/
  521. void ReleaseEditScrap( EditWindowPtr dWin, EditChunk ***scrap )
  522. {
  523.     EditChunk    **cc, **bc;
  524.     cc = *scrap;
  525.     while( cc )
  526.     {
  527.         bc = ( *cc )->next;
  528.         DisposeChunk( dWin, cc );
  529.         cc = bc;
  530.     }
  531.     *scrap = NULL;
  532. }
  533.  
  534. // High Level Copy
  535.  
  536. /*** COPY SELECTION ***/
  537. void CopySelection( EditWindowPtr dWin )
  538. {
  539. #if !TARGET_API_MAC_CARBON
  540. // LR: v1.6.5    PScrapStuff ScrapInfo;    // LR: see below
  541. #endif
  542.  
  543.     CopyOperation( dWin, &_scrapChunk );
  544.     if( _scrapChunk )
  545.     {
  546.         OSErr anErr;
  547.  
  548. #if TARGET_API_MAC_CARBON
  549.         ScrapRef scrapRef;
  550.  
  551.         anErr = ClearCurrentScrap();
  552.         if( !anErr )
  553.             anErr = GetCurrentScrap( &scrapRef );
  554. #else
  555.         ZeroScrap();
  556. #endif
  557.  
  558.         // LR: -- sure wish I remembered who sent me this code!!!
  559.  
  560.         if( EM_Hex == dWin->editMode )
  561.         {
  562.             Handle tmp;
  563.             const char *src;
  564.             char *dest, bit;
  565.             long i, len = ( *_scrapChunk )->size * (gPrefs.formatCopies ? 3 : 2);    //LR 1.72 -- size depends on how copied
  566.  
  567.             tmp = NewHandle( len );
  568.             if( tmp )
  569.             {
  570.                 HLock( ( *_scrapChunk )->data );
  571.                 HLock( tmp );
  572.  
  573.                 src = ( const char * ) *( *_scrapChunk )->data;
  574.                 dest = *tmp;
  575.  
  576.                 len = ( *_scrapChunk )->size;
  577.                 for( i=0; i<len; ++i, ++src )
  578.                 {
  579.                      bit = ( src[0] & 0xF0 ) >> 4;
  580.                      *dest++ = bit > 9 ? ( bit-10+'A' ) : ( bit+'0' );
  581.                      bit = ( src[0] & 0x0F );
  582.                      *dest++ = bit > 9 ? ( bit-10+'A' ) : ( bit+'0' );
  583.                      if( gPrefs.formatCopies )
  584.                          *dest++ = (i + 1) % kBytesPerLine == 0 ? '\r' : ' ';
  585.                 }
  586.  
  587.                 HUnlock( ( *_scrapChunk )->data );
  588.  
  589. #if TARGET_API_MAC_CARBON
  590.                 anErr = PutScrapFlavor( scrapRef, kScrapFlavorTypeText, kScrapFlavorMaskNone, GetHandleSize( tmp ), *tmp );
  591. #else
  592.                 anErr = PutScrap( GetHandleSize( tmp ), kScrapFlavorTypeText, *tmp );
  593. #endif
  594.                 HUnlock( tmp );
  595.                 DisposeHandle( tmp );
  596.             }
  597.         }
  598.         else
  599.         {
  600.              HLock( ( *_scrapChunk )->data );
  601. #if TARGET_API_MAC_CARBON
  602.             anErr = PutScrapFlavor( scrapRef, kScrapFlavorTypeText, kScrapFlavorMaskNone, (*_scrapChunk)->size, *(*_scrapChunk)->data );
  603. #else
  604.             anErr = PutScrap( (*_scrapChunk)->size, kScrapFlavorTypeText, *(*_scrapChunk)->data );
  605. #endif
  606.              HUnlock( ( *_scrapChunk )->data );
  607.         }
  608.  
  609.          // LR: the "correct" way to do things ( UniversalHeaders )
  610. /* 1.65 LR -- this is not needed for anything
  611. #if TARGET_API_MAC_CARBON
  612.         {
  613.             anErr = GetScrapFlavorSize( gScrapRef, kScrapFlavorTypeText, &gScrapCount );
  614.         }
  615. #else
  616.          ScrapInfo = InfoScrap();
  617.          gScrapCount = ScrapInfo->scrapCount;
  618. //         gScrapCount = ScrapInfo.scrapCount;
  619. #endif
  620. */
  621.          ( *_scrapChunk )->lastCtr = 0;    // Flag as shorternal
  622.  
  623.     }
  624. }
  625.  
  626.  
  627. /*** COPY OPERATION ***/
  628. void CopyOperation( EditWindowPtr dWin, EditChunk ***scrapChunk )
  629. {
  630.     EditChunk    **fc, **ec, **nc, **tc;
  631.     // Unload current scrap
  632.     ReleaseEditScrap( dWin, scrapChunk );
  633.  
  634.     // Copy current selection into scrapChunk
  635.     // Identify Starting Chunk
  636.     fc = GetChunkByAddr( dWin, dWin->startSel );
  637.     dWin->curChunk = fc;        // Optimize chunk searches
  638.  
  639.     // Identify Ending Chunk
  640.     ec = GetChunkByAddr( dWin, dWin->endSel );
  641.  
  642.     // If Chunks are the same
  643.     nc = NewChunk( dWin->endSel - dWin->startSel, 0, 0, CT_Unwritten );
  644.     if( !nc ) return;
  645.  
  646.     *scrapChunk = nc;
  647.  
  648.     if( fc == ec )
  649.     {
  650.         LoadChunk( dWin, fc );
  651.         BlockMove( *( *fc )->data + ( dWin->startSel - ( *fc )->addr ), 
  652.                     *( *nc )->data, ( *nc )->size );
  653.     }
  654.     else
  655.     {
  656.         // First Chunk to End
  657.         tc = fc;
  658.         LoadChunk( dWin, tc );
  659.         BlockMove( *( *tc )->data + ( dWin->startSel - ( *tc )->addr ), *( *nc )->data, 
  660.                     ( *tc )->size - ( dWin->startSel - ( *tc )->addr ) );
  661.         tc = ( *tc )->next;
  662.  
  663.         // Middle Chunks, If Any
  664.         while( tc != ec )
  665.         {
  666.             LoadChunk( dWin, tc );
  667.             BlockMove( *( *tc )->data, *( *nc )->data + ( ( *tc )->addr - dWin->startSel ), 
  668.                         ( *tc )->size );
  669.             tc = ( *tc )->next;
  670.         }
  671.  
  672.         // Last Chunk
  673.         LoadChunk( dWin, tc );
  674.         BlockMove( *( *tc )->data, *( *nc )->data + ( ( *tc )->addr - dWin->startSel ), 
  675.                     dWin->endSel - ( *tc )->addr );
  676.     }
  677. }
  678.  
  679. /*** CUT SELECTION ***/
  680. void CutSelection( EditWindowPtr dWin )
  681. {
  682.     //LR 180 -- first, this is useless on read-only files!
  683.     if( dWin->readOnlyFlag )
  684.     {
  685.         ErrorAlert( ES_Stop, errReadOnly );
  686.         return;
  687.     }
  688.  
  689.     RememberOperation( dWin, EO_Cut, &gUndo );
  690.     CopySelection( dWin );        // Copy into paste buffer (180 -- copy selection, not just operation!)
  691.     RemoveSelection( dWin );
  692.     ScrollToSelection( dWin, dWin->startSel, false );
  693. }
  694.  
  695. // LR: v1.6.5 -- New code for getting scrap in Carbon & Classic styles. Here
  696. // because it didn't make sense to grab it all the time in the idle routine
  697.  
  698. /*** MY GET SCRAP ***/
  699. void MyGetScrap( EditWindowPtr dWin )
  700. {
  701.     long scrapSize = 0;
  702.     OSErr anErr;
  703.  
  704. #if TARGET_API_MAC_CARBON
  705.     ScrapRef scrapRef;
  706.     ScrapFlavorFlags flavorFlags;
  707.  
  708.     anErr = GetCurrentScrap( &scrapRef );
  709.     if( !anErr )
  710.         anErr = GetScrapFlavorFlags( scrapRef, kScrapFlavorTypeText, &flavorFlags );        // non-blocking check for scrap data
  711.     if( !anErr )
  712.         anErr = GetScrapFlavorSize( scrapRef, kScrapFlavorTypeText, &scrapSize );    // blocking call to get size
  713. #else
  714.     long        offset;
  715.  
  716.     scrapSize = GetScrap( NULL, kScrapFlavorTypeText, &offset );
  717. #endif
  718.  
  719.     if( scrapSize > 0 )
  720.     {
  721.         EditChunk    **nc;
  722.  
  723.         nc = NewChunk( scrapSize, 0, 0, CT_Unwritten );
  724.         if( !nc ) ErrorAlert( ES_Caution, errMemory );
  725.         else
  726.         {
  727.             ReleaseEditScrap( dWin, &_scrapChunk );
  728.             _scrapChunk = nc;
  729.  
  730.             HLock( (*_scrapChunk)->data );
  731. #if TARGET_API_MAC_CARBON
  732.             anErr = GetScrapFlavorData( scrapRef, kScrapFlavorTypeText, &scrapSize, *(*_scrapChunk)->data );
  733. #else
  734.             anErr = GetScrap( (*_scrapChunk)->data, kScrapFlavorTypeText, &offset );
  735. #endif
  736.  
  737.             HUnlock( (*_scrapChunk)->data );
  738.             if( anErr >= 0 )
  739.                 ( *_scrapChunk )->lastCtr = 1;    // Flag as external
  740.             else
  741.             {
  742.                 ReleaseEditScrap( dWin, &_scrapChunk );    // error!
  743.                 ErrorAlert( ES_Caution, errPaste, (int)anErr );
  744.             }
  745.         }
  746.     }
  747. }
  748.  
  749. // High Level Paste
  750.  
  751. /*** PASTE SELECTION ***/
  752. void PasteSelection( EditWindowPtr dWin )
  753. {
  754.     //LR 180 -- first, this is useless on read-only files!
  755.     if( dWin->readOnlyFlag )
  756.     {
  757.         ErrorAlert( ES_Stop, errReadOnly );
  758.         return;
  759.     }
  760.  
  761.     MyGetScrap( dWin );    // LR: v1.6.5 get scrap only as needed
  762.  
  763.     if( _scrapChunk )    // LR: 1.7 due to bug (?) in Carbon, scrap may not be available!
  764.     {
  765.         // LR: v1.6.5 moved from PasteOperation to avoid bad Undo
  766.         // Hex Pasting Mode for Outside Pastes
  767.         if( EM_Hex == dWin->editMode && ( *_scrapChunk )->lastCtr == 1 )
  768.         {
  769. // LR: v1.6.5 failure not a problem!        if( !HexConvertScrap( dWin, _scrapChunk ) ) return;
  770.             HexConvertScrap( dWin, _scrapChunk );
  771.         }
  772.  
  773.         // Do actual paste
  774.         RememberOperation( dWin, EO_Paste, &gUndo );
  775.  
  776.         PasteOperation( dWin, _scrapChunk );
  777.         ScrollToSelection( dWin, dWin->startSel, false );
  778.     }
  779. }
  780.  
  781. /*** HEX CONVERT SCRAP ***/
  782. Boolean HexConvertScrap( EditWindowPtr dWin, EditChunk **scrapChunk )
  783. {
  784.     #pragma unused( dWin )    // LR: fix warnings
  785.  
  786.     Handle    rh = NULL;
  787.     Ptr        sp, dp, esp;
  788.     short    val;
  789.     Boolean    loFlag;
  790.  
  791.     rh = NewHandle( ( *scrapChunk )->size );
  792.     if( !rh )
  793.     {
  794.         ErrorAlert( ES_Caution, errMemory );
  795.         return false;
  796.     }
  797.     HLock( rh );
  798.     HLock( ( *scrapChunk )->data );
  799.     sp = *( *scrapChunk )->data;
  800.     esp = sp + ( *scrapChunk )->size;
  801.     dp = *rh;
  802.     loFlag = false;
  803.     for( ; sp < esp; ++sp )
  804.     {
  805.         if( *sp == '0' && *( sp +1 ) == 'x' )
  806.         {
  807.             loFlag = 0;
  808.             ++sp;
  809.             continue;
  810.         }
  811.         if( isspace( *sp ) || ispunct( *sp ) )
  812.         {
  813.             loFlag = 0;
  814.             continue;
  815.         }
  816.         if( *sp >= '0' && *sp <= '9' )        val = *sp - '0';
  817.         else if( *sp >= 'A' && *sp <= 'F' )    val = 0x0A + ( *sp - 'A' );
  818.         else if( *sp >= 'a' && *sp <= 'f' )    val = 0x0A + ( *sp - 'a' );
  819.         else goto HexError;
  820.         if( loFlag )
  821.         {
  822.             *( dp-1 ) = ( *( dp-1 ) << 4 ) | val;
  823.             loFlag = 0;
  824.         }            
  825.         else
  826.         {
  827.             *dp = val;
  828.             ++dp;
  829.             loFlag = 1;
  830.         }
  831.     }
  832.     if( dp - *rh == 0 ) goto HexError;
  833.  
  834.     ( *scrapChunk )->size = dp - *rh;
  835.  
  836. // LR: v1.6.5    HUnlock( rh );
  837.     BlockMove( *rh, *( *scrapChunk )->data, ( *scrapChunk )->size );
  838.     HUnlock( ( *scrapChunk )->data );
  839.     DisposeHandle( rh );
  840.  
  841.     ( *scrapChunk )->lastCtr = 0;        // Mark as internal
  842.     return true;
  843.     
  844. HexError:
  845. // LR: v1.6.5    HUnlock( rh );
  846.     DisposeHandle( rh );
  847.     HUnlock( ( *scrapChunk )->data );
  848. // LR: v1.6.5 no need to alert user, we paste anyway!    ErrorAlert( ES_Caution, "Only valid Hex values may be pasted here" );
  849.     return false;
  850. }
  851.  
  852. void PasteOperation( EditWindowPtr dWin, EditChunk **scrapChunk )
  853. {
  854.     EditChunk **fc, **ec, **nc;
  855.  
  856.     //LR 180 -- first, this is useless on read-only files!
  857.     if( dWin->readOnlyFlag )
  858.     {
  859.         ErrorAlert( ES_Stop, errReadOnly );
  860.         return;
  861.     }
  862.  
  863.     // Create duplicate scrap attached to nc->nec
  864.     nc = NewChunk( ( *scrapChunk )->size, 0, 0, CT_Unwritten );
  865.     if( !nc ) return;
  866.  
  867.     BlockMove( *( *scrapChunk )->data, *( *nc )->data, ( *nc )->size );
  868.  
  869.     RemoveSelection( dWin );
  870.  
  871.     // Insert paste buffer into selStart
  872.  
  873.     fc = GetChunkByAddr( dWin, dWin->startSel );
  874.     if( ( *fc )->addr < dWin->startSel )
  875.     {
  876.         // Split 'em up
  877.         // Unload it
  878.         UnloadChunk( dWin, fc, true );
  879.  
  880.         // Split into two chunks
  881.         if( dWin->startSel < ( *fc )->addr + ( *fc )->size )
  882.         {
  883.             ec = NewChunk( ( *fc )->size - ( dWin->startSel - ( *fc )->addr ), 0, 
  884.                             ( *fc )->filePos + ( dWin->startSel - ( *fc )->addr ), ( *fc )->type );
  885.             ( *ec )->prev = fc;
  886.             ( *ec )->next = ( *fc )->next;
  887.             if( ( *ec )->next ) ( *( *ec )->next )->prev = ec;
  888.         }
  889.         else ec = ( *fc )->next;
  890.  
  891.         ( *fc )->next = ec;
  892.         ( *fc )->size = dWin->startSel - ( *fc )->addr;
  893.     }
  894.     else
  895.     {
  896.         ec = fc;
  897.         fc = ( *fc )->prev;
  898.     }
  899.  
  900.     // Insert fc->nc->ec
  901.     if( fc )
  902.     {
  903.         ( *fc )->next = nc;
  904.         ( *nc )->prev = fc;
  905.         ( *nc )->addr = ( *fc )->addr + ( *fc )->size;
  906.     }
  907.     else
  908.     {
  909.         dWin->firstChunk = nc;
  910.         ( *nc )->addr = 0L;
  911.     }
  912.  
  913.     if( ec )
  914.     {
  915.         ( *nc )->next = ec;
  916.         ( *ec )->prev = nc;
  917.     }
  918.  
  919.     // Correct addresses
  920.     RewriteAddressChain( nc );
  921.  
  922.     // Reset Selection
  923.     dWin->startSel = dWin->endSel = ( *nc )->addr + ( *nc )->size;
  924.  
  925.     // Update other stuff
  926.     dWin->fileSize += ( *scrapChunk )->size;
  927.     dWin->dirtyFlag = true;
  928. }
  929.  
  930. /*** DELETE SELECTION ***/
  931. void DeleteSelection( EditWindowPtr dWin )
  932. {
  933.     //LR 190 -- renamed from ClearSelection, more appropriate name
  934.     if( dWin->readOnlyFlag )
  935.     {
  936.         ErrorAlert( ES_Stop, errReadOnly );
  937.         return;
  938.     }
  939.  
  940.     RememberOperation( dWin, EO_Clear, &gUndo );
  941.     RemoveSelection( dWin );
  942.     ScrollToSelection( dWin, dWin->startSel, false );
  943. }
  944.  
  945. /*** CLEAR SELECTION ***/
  946. void ClearSelection( EditWindowPtr dWin )
  947. {
  948.     //LR 190 -- clear selection should "clear" bytes, not delete them.
  949.     if( dWin->readOnlyFlag )
  950.     {
  951.         ErrorAlert( ES_Stop, errReadOnly );
  952.         return;
  953.     }
  954.  
  955.     if( dWin->endSel > dWin->startSel )    // can only clear something if it's selected
  956.     {
  957.         // Create a new chunk which will be all zero by default
  958.         EditChunk **tc = NewChunk( dWin->endSel - dWin->startSel, 0, 0, CT_Unwritten );
  959.         if( !tc )
  960.             ErrorAlert( ES_Caution, errMemory );
  961.         else
  962.         {
  963.             int hold = dWin->startSel;        //LR 191 -- paste moves insertion point
  964.  
  965.             (*tc)->lastCtr = 1;    // external chunk
  966.  
  967.             // now, remember for undo and past this chunk over existing space, then free the memory used
  968.             RememberOperation( dWin, EO_Paste, &gUndo );
  969.             PasteOperation( dWin, tc );
  970.             DisposeChunk( dWin, tc );
  971.  
  972.             dWin->startSel = hold;
  973.         }
  974.         ScrollToSelection( dWin, dWin->startSel, false );
  975.     }
  976.     else
  977.         SysBeep(0);        // nothing to clear, signal it!
  978. }
  979.  
  980. // Remember current state for Undo of following operation
  981.  
  982. /*** REMEMBER OPERATION ***/
  983. void RememberOperation( EditWindowPtr dWin, short opType, UndoPtr ur )
  984. {
  985.     Str31    undoStr, menuStr;
  986.     MenuRef editMenu;
  987.  
  988.     //LR: 1.66 - total re-write to be localizable!
  989.  
  990.     // Assume undo
  991.     GetIndString( menuStr, strUndo, EO_Undo );
  992.  
  993.     // check for Redo (ie, if Undo change to Redo)
  994.     editMenu = GetMenuRef( kEditMenu );
  995.     if( ur == &gRedo )
  996.     {
  997.         Str31 tempStr;
  998.  
  999.         GetMenuItemText( editMenu, EM_Undo, tempStr );
  1000.         if( tempStr[1] == 'U' )
  1001.             GetIndString( menuStr, strUndo, EO_Redo );
  1002.     }
  1003.  
  1004.     // Now, get operation string and create menu string
  1005.     GetIndString( undoStr, strUndo, opType );
  1006.     BlockMove( &undoStr[1], &menuStr[menuStr[0] + 1], undoStr[0] );
  1007.     menuStr[0] += undoStr[0];
  1008.     SetMenuItemText( editMenu, EM_Undo, menuStr );
  1009.  
  1010.     ReleaseEditScrap( dWin, &ur->undoScrap );
  1011.  
  1012.     // Clear Undo Stuff
  1013.     ur->undoScrap = NULL;
  1014.     ur->type = opType;
  1015.     ur->startSel = dWin->startSel;
  1016.     ur->endSel = dWin->endSel;
  1017.     ur->fileSize = dWin->fileSize;
  1018.     ur->theWin = dWin;
  1019.  
  1020.     CopyOperation( dWin, &ur->undoScrap );
  1021.  
  1022.     ( *ur->undoScrap )->lastCtr= 0;
  1023.  
  1024.     dWin->lastTypePos = -1;    // Clear Special Editing Modes
  1025.     dWin->loByteFlag = false;
  1026. }
  1027.  
  1028. /*** UNDO OPERATION ***/
  1029. void UndoOperation( void )
  1030. {
  1031.     WindowRef win;
  1032.     EditWindowPtr dWin;
  1033.  
  1034.     if( !gUndo.theWin )    //LR: 1.66 -- can be NULL!
  1035.         return;
  1036.  
  1037.     dWin = gUndo.theWin;
  1038.     if( gUndo.type == 0 ) return;
  1039.  
  1040.     //LR: 1.66 check for null front window!
  1041.     win = FrontNonFloatingWindow();
  1042.     if( !win || dWin != (EditWindowPtr)GetWRefCon( win ) )
  1043.         SelectWindow( dWin->oWin.theWin );
  1044.     
  1045.     switch( gUndo.type )
  1046.     {
  1047.         case EO_Typing:
  1048.         case EO_Paste:
  1049.         case EO_Insert:
  1050.             dWin->startSel = gUndo.startSel;
  1051.             dWin->endSel = dWin->fileSize - ( gUndo.fileSize - gUndo.endSel );
  1052.             RememberOperation( dWin, EO_Delete, &gRedo );
  1053.             RemoveSelection( dWin );
  1054.             PasteOperation( dWin, gUndo.undoScrap );
  1055.             break;
  1056.         case EO_Cut:
  1057.         case EO_Clear:
  1058.         case EO_Delete:
  1059.             dWin->startSel = dWin->endSel = gUndo.startSel;
  1060.             RememberOperation( dWin, EO_Insert, &gRedo );
  1061.             PasteOperation( dWin, gUndo.undoScrap );
  1062.             break;
  1063.     }
  1064.  
  1065.     ReleaseEditScrap( dWin, &gUndo.undoScrap );
  1066.     gUndo = gRedo;
  1067.     gRedo.undoScrap = NULL;
  1068.  
  1069.     ScrollToSelection( dWin, dWin->startSel, false );
  1070. }